package sivin.uikit.widget

import android.annotation.SuppressLint
import android.content.Context
import android.os.SystemClock
import android.os.Trace
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityManager
import android.view.animation.Interpolator
import android.widget.OverScroller
import androidx.annotation.IntDef
import androidx.core.view.accessibility.AccessibilityEventCompat
import androidx.recyclerview.widget.RecyclerView

class XRecyclerView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {

    companion object {
        const val TAG: String = "XRecyclerView"
        const val TRACE_ON_LAYOUT_TAG: String = "XRecyclerView OnLayout"
        private const val sDebugAssertionsEnabled = false
        private const val sVerboseLoggingEnabled = false

        /**
         * 当前RecyclerView is not currently scrolling
         */
        const val SCROLL_STATE_IDLE = 0

        /**
         * the RecyclerView is currently being dragged by outside input such as touch input
         */
        const val SCROLL_STATE_DRAGGING = 1

        /**
         * The RecyclerView is currently animating to a final position while not under outside control
         */
        const val SCROLL_STATE_SETTLING = 2

        val sQuinticInterpolator = Interpolator { t ->
            val v = t - 1.0f
            v * v * v * v * v + 1.0f
        }

    }

    var mChildHelper: XChildHelper? = null
    var mAdapter: RecyclerView.Adapter<*>? = null
    private var mLayout: XLayoutManager? = null

    /*
     * 这些字段仅用于跟踪当XRecyclerView在onLayout阶段是否需要layout和measure 其子视图
     * LayoutManager#isAutoMeasureEnabled()和CollectionView的测量模式都是MeasureSpec.EXACTLY时
     * 我们会跳过子视图的measure和layout，直接到布局阶段
     *
     */
    private var mLastAutoMeasureSkippedDueToExact = false
    private var mFirstLayoutComplete: Boolean = false

    /*
     * 用来跟踪嵌套调用 startInterceptRequestLayout() 和 stopInterceptRequestLayout(boolean) 方法的深度。
     * 它基本上是在计算当前拦截布局请求的次数
     * 这个变量用于判断是否应该延迟处理由 RecyclerView 的子视图引发的布局操作。
     * 如果 mInterceptRequestLayoutDepth 大于零，表示有未完成的布局拦截，此时可能会延迟布局请求的执行。
     * 每次调用 startInterceptRequestLayout() 时，这个变量的值会增加
     * 每次调用 stopInterceptRequestLayout(boolean) 时，这个变量的值会减少。
     */
    private var mInterceptRequestLayoutDepth: Int = 0

    private var mLayoutSuppressed: Boolean = false
    private var mIgnoreMotionEventTillDown = false

    private var mLayoutWasDeferred: Boolean = false

    /**
     * 这个变量会在dispatchLayout或者scroll期间增加，有写方法不能再adapter数据发生改变的时候调用，
     * 否则会产生难以调试的bug，所以最好的方式就是检查这种情况，同时抛出异常
     */
    private var mLayoutOrScrollCounter = 0

    //和mLayoutOrScrollCounter功能类似，但这个只会打印出警告日志，不会抛出异常
    private var mDispatchScrollCounter = 0

    private var mScrollState = SCROLL_STATE_IDLE

    private var mEatenAccessibilityChangeFlags = 0

    private val mAccessibilityManager: AccessibilityManager by lazy {
        val manager =
            context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
        manager
    }


    private val mState: State = State()

    val mViewFlinger = ViewFlinger()

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        mLayout?.let { layout ->
            if (layout.isAutoMeasureEnabled) {
                val widthMode = MeasureSpec.getMode(widthMeasureSpec)
                val heightMode = MeasureSpec.getMode(heightMeasureSpec)
                defaultOnMeasure(widthMeasureSpec, heightMeasureSpec)
                mLastAutoMeasureSkippedDueToExact =
                    widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY
                if (mLastAutoMeasureSkippedDueToExact || mAdapter == null) {
                    return
                }
                if (mState.mLayoutStep == State.STEP_START) {
                    dispatchLayoutStep1()
                }
                //在第二步中设置测量空间。为了保持一致性，应该使用旧尺寸进行预布局
                layout.setMeasureSpecs(widthMeasureSpec, heightMeasureSpec)
                mState.mIsMeasuring = true
                dispatchLayoutStep2()
            }
        } ?: run {
            defaultOnMeasure(widthMeasureSpec, heightMeasureSpec)
        }

    }


    /*
        1.处理Adapter更新;
        2.决定是否执行ItemAnimator;
        3.保存ItemView的动画信息。
        本方法也被称为preLayout(预布局)，当Adapter更新了，
        这个方法会保存每个ItemView的旧信息(oldViewHolderInfo)
     */
    private fun dispatchLayoutStep1() {
        mState.assertLayoutStep(State.STEP_START)
        //TODO:
        mState.mLayoutStep = State.STEP_LAYOUT
    }


    /*
      在这个方法里面，真正进行children的测量和布局
     */
    private fun dispatchLayoutStep2() {
        startInterceptRequestLayout()
        onEnterLayoutOrScroll()
        mState.assertLayoutStep(State.STEP_LAYOUT or State.STEP_ANIMATIONS)

    }

    private fun startInterceptRequestLayout() {
        mInterceptRequestLayoutDepth++
        if (mInterceptRequestLayoutDepth == 1 && !mLayoutSuppressed) {
            mLayoutWasDeferred = false
        }
    }


    @SuppressLint("UnclosedTrace")
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        Trace.beginSection(TRACE_ON_LAYOUT_TAG)
        dispatchLayout()
        mFirstLayoutComplete = true
        Trace.endSection()
    }

    private fun dispatchLayout() {
        if (mAdapter == null) {
            Log.w(TAG, "No adapter attached; skipping layout")
            // leave the state in START
            return
        }
        if (mLayout == null) {

        }
    }

    override
    fun suppressLayout(suppress: Boolean) {
        if (suppress != mLayoutSuppressed) {
            assertNotInLayoutOrScroll("Do not suppressLayout in layout or scroll")
            if (!suppress) {
                mLayoutSuppressed = false
                if (mLayoutWasDeferred && mLayout != null && mAdapter != null) {
                    requestLayout()
                }
                mLayoutWasDeferred = false
            } else {
                val now: Long = SystemClock.uptimeMillis()
                val cancelEvent = MotionEvent.obtain(
                    now, now, MotionEvent.ACTION_CANCEL,
                    0.0f, 0.0f, 0
                )
                onTouchEvent(cancelEvent)
                mLayoutSuppressed = true
                mIgnoreMotionEventTillDown = true
                stopScroll()
            }
        }
    }

    fun stopScroll() {
        setScrollState(SCROLL_STATE_IDLE)
    }

    private fun setScrollState(state: Int) {
        if (state == mScrollState) {
            return
        }
        if (sVerboseLoggingEnabled) {
            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState, Exception());
        }
        mScrollState = state
        if (state != SCROLL_STATE_SETTLING) {
            stopScrollersInternal()
        }
        dispatchOnScrollStateChanged(state)
    }

    private fun dispatchOnScrollStateChanged(state: Int) {

    }

    private fun stopScrollersInternal() {

    }

    private fun assertNotInLayoutOrScroll(message: String) {
        if (isComputingLayout()) {
            throw IllegalStateException(
                "Cannot call this method while RecyclerView is "
                        + "computing a layout or scrolling" + exceptionLabel()
            )
        }
        if (mDispatchScrollCounter > 0) {
            Log.w(
                TAG,
                "Cannot call this method in a scroll callback. Scroll callbacks might"
                        + "be run during a measure & layout pass where you cannot change the"
                        + "RecyclerView data. Any method call that might change the structure"
                        + "of the RecyclerView or the adapter contents should be postponed to"
                        + "the next frame.",
                java.lang.IllegalStateException("" + exceptionLabel())
            )
        }
    }

    private fun isComputingLayout(): Boolean {
        return mLayoutOrScrollCounter > 0
    }

    private fun onEnterLayoutOrScroll() {
        mLayoutOrScrollCounter++
    }

    private fun onExitLayoutOrScroll(enableChangeEvents: Boolean) {
        mLayoutOrScrollCounter--
        if (mLayoutOrScrollCounter < 1) {
            check(!(sDebugAssertionsEnabled && mLayoutOrScrollCounter < 0)) {
                ("layout or scroll counter cannot go below zero."
                        + "Some calls are not matching" + exceptionLabel())
            }
            mLayoutOrScrollCounter = 0
            if (enableChangeEvents) {
                dispatchContentChangedIfNecessary()
                dispatchPendingImportantForAccessibilityChanges()
            }
        }
    }

    private fun dispatchContentChangedIfNecessary() {
        val flags: Int = mEatenAccessibilityChangeFlags
        mEatenAccessibilityChangeFlags = 0
        if (flags != 0 && isAccessibilityEnabled()) {
            val event = AccessibilityEvent.obtain()
            event.eventType = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
            AccessibilityEventCompat.setContentChangeTypes(event, flags)
            sendAccessibilityEventUnchecked(event)
        }
    }

    private fun dispatchPendingImportantForAccessibilityChanges() {
        //TODO:
    }

    private fun isAccessibilityEnabled(): Boolean {
        return mAccessibilityManager.isEnabled
    }

    private fun exceptionLabel(): String {
        return (" " + super.toString()
                + ", adapter:" + mAdapter
                + ", layout:" + mLayout
                + ", context:" + context)
    }

    private fun defaultOnMeasure(widthSpec: Int, heightSpec: Int) {
        val width = XLayoutManager.chooseSize(
            widthSpec,
            paddingLeft + paddingRight, minimumWidth
        )
        val height = XLayoutManager.chooseSize(
            heightSpec,
            paddingTop + paddingBottom, minimumHeight
        )
        setMeasuredDimension(width, height)
    }

    class State {
        companion object {
            const val STEP_START = 1
            const val STEP_LAYOUT = 1 shl 1
            const val STEP_ANIMATIONS = 1 shl 2
        }

        @Retention(AnnotationRetention.SOURCE)
        @IntDef(flag = true, value = [STEP_START, STEP_LAYOUT, STEP_ANIMATIONS])
        annotation class LayoutState

        fun assertLayoutStep(accepted: Int) {
            if ((accepted and mLayoutStep) == 0) {
                throw IllegalStateException(
                    "Layout state should be one of "
                            + Integer.toBinaryString(accepted) + " but it is "
                            + Integer.toBinaryString(mLayoutStep)
                )
            }
        }

        @LayoutState
        var mLayoutStep: Int = STEP_START
        var mIsMeasuring = false
    }

    abstract class XLayoutManager {
        private var mWidthMode = 0
        private var mHeightMode = 0
        private var mWidth = 0
        private var mHeight = 0
        private var mRecyclerView: XRecyclerView? = null
        private var mChildHelper: XChildHelper? = null
        var isAutoMeasureEnabled: Boolean = false

        fun setXRecyclerView(recyclerView: XRecyclerView?) {
            if (recyclerView == null) {
                mRecyclerView = null
                mChildHelper = null
                mWidth = 0
                mHeight = 0
            } else {
                mRecyclerView = recyclerView
                mChildHelper = recyclerView.mChildHelper
                mWidth = recyclerView.width
                mHeight = recyclerView.height
            }
            mWidthMode = MeasureSpec.EXACTLY
            mHeightMode = MeasureSpec.EXACTLY
        }

        fun setMeasureSpecs(widthMeasureSpec: Int, heightMeasureSpec: Int) {
            mWidth = MeasureSpec.getSize(widthMeasureSpec)
            mHeight = MeasureSpec.getSize(heightMeasureSpec)
        }


        companion object {
            fun chooseSize(spec: Int, desired: Int, min: Int): Int {
                val mode = MeasureSpec.getMode(spec)
                val size = MeasureSpec.getSize(spec)
                return when (mode) {
                    MeasureSpec.EXACTLY -> {
                        size
                    }

                    MeasureSpec.AT_MOST -> {
                        size.coerceAtMost(desired.coerceAtLeast(min))
                    }

                    MeasureSpec.UNSPECIFIED -> {
                        desired.coerceAtLeast(min)
                    }

                    else -> {
                        //强迫desired的值最小是min
                        desired.coerceAtLeast(min)
                    }
                }
            }
        }
    }

    class ViewFlinger : Runnable {

        private var mLastFlingX = 0
        private var mlastFlingY = 0
        lateinit var mOverScroller: OverScroller
        val mInterpolator = sQuinticInterpolator

        override fun run() {

        }

    }
}